#include "connection_manager.h"

#include <unistd.h>
#include <cstring>
#include <cstdlib>

class TLock
{
public:
    TLock(pthread_mutex_t& mutex)
    : Mutex(mutex)
    {
        pthread_mutex_lock(&Mutex);
    }

    ~TLock()
    {
        pthread_mutex_unlock(&Mutex);
    }
private:
    pthread_mutex_t& Mutex;
};

bool TConnectionManager::Init(unsigned int unservedMaxSize)
{
    UnservedMaxSize =   unservedMaxSize;
    UnservedConnections.reserve(UnservedMaxSize);

    ConnectionQueueMutex    =   static_cast<pthread_mutex_t*>(malloc(sizeof(pthread_mutex_t)));
    bzero(static_cast<void*>(ConnectionQueueMutex), sizeof(ConnectionQueueMutex));
    if (0 != pthread_mutex_init(ConnectionQueueMutex, NULL)) {
        return false;
    }

    IsHaveNewMessage    =   static_cast<pthread_cond_t*>(malloc(sizeof(pthread_cond_t)));
    bzero(static_cast<void*>(IsHaveNewMessage), sizeof(ConnectionQueueMutex));
    if (0 != pthread_cond_init(IsHaveNewMessage, NULL)) {
        return false;
    }

    return true;
}

TConnectionManager::~TConnectionManager()
{
    if (NULL != ConnectionQueueMutex) {
        pthread_mutex_destroy(ConnectionQueueMutex);
        free(ConnectionQueueMutex);
    }

    if (NULL != IsHaveNewMessage) {
        pthread_cond_destroy(IsHaveNewMessage);
        free(IsHaveNewMessage);
    }

    for ( unsigned int connNum = 0
        ; connNum != UnservedConnections.size()
        ; ++connNum) {
        close(UnservedConnections[connNum]);
    }
}

bool TConnectionManager::AddConnection(int connectionId)
{
    TLock   lockQueue(*ConnectionQueueMutex);

    if (UnservedConnections.size() >= UnservedMaxSize) {
        //TODO say service unavailible
        close(connectionId);
        return false;
    }
    bool    isEmpty =   UnservedConnections.empty();
    UnservedConnections.push_back(connectionId);
    if (isEmpty) {
        pthread_cond_signal(IsHaveNewMessage);
    }
    return true;
}

int TConnectionManager::GetUnservedConection()
{
    TLock   lockQueue(*ConnectionQueueMutex);

    while ((UnservedConnections.empty()) && (!IsStoped)) {
        pthread_cond_wait(IsHaveNewMessage, ConnectionQueueMutex);
    }

    int connectionId    =   KStopSignal;
    if (!IsStoped) {
        connectionId    =   UnservedConnections.front();
        UnservedConnections.erase(UnservedConnections.begin());
    }

    return connectionId;
}

void TConnectionManager::Stop()
{
    if (NULL == ConnectionQueueMutex) {
        return;
    }

    if (NULL == IsHaveNewMessage) {
        return;
    }

    TLock   lockQueue(*ConnectionQueueMutex);
    IsStoped    =   true;
    pthread_cond_signal(IsHaveNewMessage);
}
